package robocup.component.worldmodel;

import java.util.LinkedList;

import robocup.component.PConf;
import robocup.component.WorldModel;
import robocup.component.actions.Action;
import robocup.component.geometry.Vektor;
import robocup.component.infotypes.CoachSayInfo;
import robocup.component.infotypes.InfoReceiver;
import robocup.component.infotypes.PlayModeInfo;
import robocup.component.infotypes.PlayerSayInfo;
import robocup.component.infotypes.PlayerTypesInfo;
import robocup.component.infotypes.RefereeInfo;
import robocup.component.infotypes.SenseBodyInfo;
import robocup.component.infotypes.VisualInfo;
import robocup.component.speechacts.coachlanguage.ActivationElement;
import robocup.component.speechacts.coachlanguage.CPoint;
import robocup.component.speechacts.coachlanguage.Condition;
import robocup.component.speechacts.coachlanguage.DefineDirective;
import robocup.component.speechacts.coachlanguage.DefineMessage;
import robocup.component.speechacts.coachlanguage.DefineRegion;
import robocup.component.speechacts.coachlanguage.DefineRule;
import robocup.component.speechacts.coachlanguage.DeleteMessage;
import robocup.component.speechacts.coachlanguage.Directive;
import robocup.component.speechacts.coachlanguage.Region;
import robocup.component.speechacts.coachlanguage.RuleMessage;
import robocup.component.speechacts.coachlanguage.TypeConstants;
import robocup.component.speechacts.coachlanguage.UnumSet;

public class CLangModel
    implements InfoReceiver {

  private final WorldModel               world;

  private final Action                   action      = null;

  private final LinkedList<CoachSayInfo> clangList   = new LinkedList<CoachSayInfo>();

  private final LinkedList<DefineRule>   ruleListOn  = new LinkedList<DefineRule>();

  private final LinkedList<DefineRule>   ruleListOff = new LinkedList<DefineRule>();

  private final Vektor[]                 home        = new Vektor[11];

  private final Vektor[]                 attraction  = new Vektor[11];

  public CLangModel(
      final WorldModel world) {

    this.world = world;

    for (int i = 0; i < 11; i++) {
      this.home[i] = null;
      this.attraction[i] = null;
    }
  }

  /**
   * update model after coach say info arrival.
   * 
   * @param info
   */
  public void update(final CoachSayInfo info) {

    final TypeConstants.MESSAGETYPE type = info.getMessageType();

    switch (type) {

      case DELETE:
        this.deleteRule(info.getDeleteMessage());
        break;

      case RULE:
        this.ruleActivation(info.getRuleMessage());
        break;

      case DEFINE:

        for (final DefineMessage dm : info.getDefineList()) {
          final TypeConstants.DEFINETYPE define = dm.getDefineType();

          switch (define) {

            case DEFINED:
              this.evaluateDirectiveMessage(dm.getDefineDirective());
              break;

            case DEFINER:
              this.evaluateRegionMessage(dm.getDefineRegion());
              break;

            case DEFINERULE:
              this.ruleListOff.add(dm.getDefineRule());
              break;

            default:
              System.out.println(info.toString());
              break;
          }

        }

        break;

      default:
        System.out.println(info.toString());
        break;

    }

  }

  public void evaluateRegionMessage(final DefineRegion dr) {

    this.evaluateRegion(dr.getRegion());
  }

  public void evaluateRegion(final Region reg) {

    final TypeConstants.REGIONTYPE type = reg.getRegionType();

    switch (type) {

      case POINT:
        this.evaluatePoint(reg.getPoint());

        break;

    }
  }

  public Vektor evaluatePoint(final CPoint pt) {

    final TypeConstants.POINTTYPE type = pt.getPointType();

    final Vektor v = new Vektor();

    switch (type) {

      case XY:
        v.setX(pt.getX());
        v.setY(pt.getY());
        break;
    }
    return v;

  }

  public void evaluateDirectiveMessage(final DefineDirective dd) {

    if (dd.getDirectiveName().matches("\"OPP_PTypes\"")) {
      this.getHetPlayerTypes(dd.getDirective());
    }

    if (dd.getDirectiveName().matches("\"OPP_Form\"")) {
      this.getOpponentFormation(dd.getDirective());
    }

  }

  public void getOpponentFormation(final Directive dir) {

    final int player = dir.getUnumSet().getUnumIntList().getFirst() - 1;
    final Vektor home = dir.getActionList().getFirst().getHomeRegion().getPoint()
        .getVektor();
    final Vektor markl = dir.getActionList().getLast().getMarklRegion().getPoint()
        .getVektor();

    this.home[player] = home;
    this.attraction[player] = markl;
  }

  public void getHetPlayerTypes(final Directive dir) {
//    if (world.getNumber() == 4)
//    	System.out.println(world.getCycleAndPlayerString() + " DIRECTIVE RECEIVED");
    final int player = dir.getUnumSet().getUnumIntList().getFirst() - 1;
    final int ptype = dir.getActionList().getFirst().getHtypeInt();

//    this.world.getOpponents()[player]
//        .setPConf((this.world.getAvailablePconfs())[ptype]);
//    this.world.getOpponents()[player].setStatusChanged(true);
    
    if (this.world.getAvailablePconfs() == null)
    	return;
    this.world.getAllPlayers()[player + 11]
        .setPConf((this.world.getAvailablePconfs())[ptype]);
    this.world.getAllPlayers()[player + 11].setStatusChanged(true);
    
    this.world.getNextPlayers()[player + 11]
        .setPConf((this.world.getAvailablePconfs())[ptype]);
    this.world.getNextPlayers()[player + 11].setStatusChanged(true);
    
    this.world.getExpectedPlayers()[player + 11]
        .setPConf((this.world.getAvailablePconfs())[ptype]);
        this.world.getExpectedPlayers()[player + 11].setStatusChanged(true);

  }

  public void ruleActivation(final RuleMessage ruleMessage) {

    final LinkedList<ActivationElement> activationList = ruleMessage
        .getActivationList();

    for (final ActivationElement ae : activationList) {
      final LinkedList<String> ruleList = ae.getRuleList().getIdList();
      if (ae.isRuleOn()) {
        if (ruleList.getFirst().matches("all")) {
          for (final DefineRule rule : this.ruleListOff) {
            this.ruleListOn.add(rule);
            this.ruleListOff.remove(rule);
          }
        }
        else {
          for (final String name : ruleList) {
            for (final DefineRule rule : this.ruleListOff) {
              if (name.matches(rule.getRuleName())) {
                this.ruleListOn.add(rule);
                this.ruleListOff.remove(rule);
              }
            }

          }
        }

      }
      else {
        if (ruleList.getFirst().matches("all")) {
          for (final DefineRule rule : this.ruleListOn) {
            this.ruleListOff.add(rule);
            this.ruleListOn.remove(rule);
          }
        }
        else {
          for (final String name : ruleList) {
            for (final DefineRule rule : this.ruleListOn) {
              if (name.matches(rule.getRuleName())) {
                this.ruleListOff.add(rule);
                this.ruleListOn.remove(rule);
              }
            }
          }
        }

      }
    }
  }

  public void deleteRule(final DeleteMessage deleteMessage) {

    final LinkedList<String> deleteList = deleteMessage.getDeleteList().getIdList();

    for (final String name : deleteList) {
      for (final DefineRule rule : this.ruleListOff) {
        if (name.matches(rule.getRuleName())) {
          this.ruleListOff.remove(rule);
        }
      }
    }

  }

  public void addCoachSayInfo(final CoachSayInfo coachSayInfo) {

    this.clangList.add(coachSayInfo);
  }

  public boolean evaluateRuleCondition() {

    boolean evaluation = false;

    for (final DefineRule dr : this.ruleListOn) {
      if (this.evaluateCondition(dr.getRule().getCondition())) {
        evaluation = true;
        this.evaluateDirectiveList(dr.getRule().getDirectiveList());
      }
    }

    return evaluation;
  }

  public void evaluateDirectiveList(final LinkedList<Directive> directiveList) {

  }

  public boolean isInUnumSet(final int player,
      final UnumSet unumSet) {

    for (final int i : unumSet.getUnumIntList()) {
      if (player == i) {
        return true;
      }
    }
    return false;
  }

  public void evaluateDirective(final Directive directive) {

    if (this.isInUnumSet(this.world.getSelfRef().getNumber(), directive
        .getUnumSet())) {
      // do anything
    }
  }

  public boolean isPosinTri(final CPoint tri1,
      final CPoint tri2,
      final CPoint tri3,
      final double x,
      final double y) {

    final double sign1 = (tri2.getY() - tri1.getY()) * (x - tri1.getX()) - (tri2
        .getX() - tri1.getX()) * (tri1.getY() - y);
    final double sign2 = (tri3.getY() - tri2.getY()) * (x - tri2.getX()) - (tri3
        .getX() - tri2.getX()) * (tri2.getY() - y);
    final double sign3 = (tri1.getY() - tri3.getY()) * (x - tri3.getX()) - (tri1
        .getX() - tri3.getX()) * (tri3.getY() - y);
    if ((sign1 >= 0 && sign2 >= 0 && sign3 >= 0) || (sign1 <= 0 && sign2 <= 0 && sign3 <= 0)) {
      return true;
    }
    else {
      return false;
    }
  }

  public boolean isPosinRegion(final Vektor vector,
      final Region region) {

    final double x = vector.getX();
    final double y = vector.getY();
    switch (region.getRegionType()) {
      case INIT:
        return false;
        // case NAME:
      case NULL:
        return false;
      case POINT:
        if (x == region.getPoint().getX() && y == region.getPoint().getY()) {
          return true;
        }
        else {
          return false;
        }
      case ARC:
        final double d = Math
            .sqrt((x - region.getArcPoint().getX()) * (x - region.getArcPoint()
                .getX()) + (y - region.getArcPoint().getY()) * (y - region
                .getArcPoint().getY()));
        if (d <= region.getArcReal2() && d >= region.getArcReal1()) {
          if (d != 0) {
            final double angle = 360 * Math.asin(y - region.getArcPoint().getY()) / d;
            if (angle <= region.getArcReal4() && angle >= region.getArcReal3()) {
              return true;
            }
            else {
              return false;
            }
          }
          else {
            return true;
          }
        }
        else {
          return false;
        }
      case REC:
        double xMax,
        xMin,
        yMax,
        yMin;
        xMax = (region.getRecPoint2().getX() >= region.getRecPoint1().getX()) ? region
            .getRecPoint2().getX() : region.getRecPoint1().getX();
        xMin = (region.getRecPoint2().getX() <= region.getRecPoint1().getX()) ? region
            .getRecPoint2().getX() : region.getRecPoint1().getX();
        yMax = (region.getRecPoint2().getY() >= region.getRecPoint1().getY()) ? region
            .getRecPoint2().getY() : region.getRecPoint1().getY();
        yMin = (region.getRecPoint2().getY() <= region.getRecPoint1().getY()) ? region
            .getRecPoint2().getY() : region.getRecPoint1().getY();
        if (x <= xMax && x >= xMin && y <= yMax && y >= yMin) {
          return true;
        }
        else {
          return false;
        }
      case TRI:
        final CPoint tri1 = region.getTriPoint1();
        final CPoint tri2 = region.getTriPoint2();
        final CPoint tri3 = region.getTriPoint3();
        return this.isPosinTri(tri1, tri2, tri3, x, y);
      case QUAD:
        final CPoint quad1 = region.getQuadPoint1();
        final CPoint quad2 = region.getQuadPoint2();
        final CPoint quad3 = region.getQuadPoint3();
        final CPoint quad4 = region.getQuadPoint4();
        return this.isPosinTri(quad1, quad2, quad3, x, y) && this.isPosinTri(
            quad3, quad4, quad1, x, y);
      case REG:
        for (final Region reg : region.getRegionList()) {
          if (this.isPosinRegion(vector, reg)) {
            return true;
          }
        }
        return false;
      default:
        return false;

    }
  }

  public boolean compare(final int A,
      final int B,
      final String com) {

    if (com == "<") {
      return A < B ? true : false;
    }
    else if (com == ">") {
      return A > B ? true : false;
    }
    else if (com == ">=") {
      return A >= B ? true : false;
    }
    else if (com == "<=") {
      return A <= B ? true : false;
    }
    else if (com == "==") {
      return A == B ? true : false;
    }
    else if (com == "!=") {
      return A != B ? true : false;
    }
    else {
      return false;
    }

  }

  public boolean evaluateCompare(final Condition com) {

    switch (com.getCondCompType()) {
      case TIME:
        return this.compare(this.world.getCycle(), com.getCondCompInt(), com
            .getCondComp());
      case OPP_GOALS:
        return this.compare(this.world.getGoalsOther(), com.getCondCompInt(),
            com.getCondComp());
      case OUR_GOALS:
        return this.compare(this.world.getGoalsTeam(), com.getCondCompInt(),
            com.getCondComp());
      case GOAL_DIFF:
        return this.compare(this.world.getGoalDiff(), com.getCondCompInt(), com
            .getCondComp());
      default:
        return false;
    }
  }

  public boolean evaluateCondition(final Condition condition) {

    final TypeConstants.CONDITIONTYPE type = condition.getConditionType();

    switch (type) {

      case TRUE:
        return true;

      case FALSE:
        return false;

      case PLAYM:
        return (this.world.getPlayMode() == condition.getPlayMode());

      case NOT:
        return (!this.evaluateCondition(condition.getNot()));

      case AND:
        boolean and = true;
        for (final Condition cond : condition.getAnd()) {
          and = this.evaluateCondition(cond);
          if (!and) {
            break;
          }
        }
        return and;

      case OR:
        boolean or = false;
        for (final Condition cond : condition.getAnd()) {
          or = this.evaluateCondition(cond);
          if (or) {
            break;
          }
        }
        return or;

      case BPOS:
        final Region bposRegion = condition.getRegion();
        final Vektor bpos = this.world.getBallRef().getPosition();
        return this.isPosinRegion(bpos, bposRegion);
        // case PPOS:
        // Region pposRegion = condition.getRegion();
      case CONDCOMP:
        return this.evaluateCompare(condition);
        // case UNUM:
        // buf.append("(unum " + this.unumString + this.unumSet.toString() +
        // ")");
        // break;
      case BOWNER:
        if (condition.isTeamOur()) {
          // UnumSet bowner = condition.getUnumSet();
          // for (int intElement : bowner.getUnumIntList()){
          //					
          // }
          // if( intElement == this.world.getBallRef().
        }
        else {

        }
    }

    return false;
  }

  public Action getAction() {

    return this.action;
  }

  /**
   * @return
   */
  public double getSuccessProbability() {

    return 0;
  }

  /**
   * @return
   */
  public double getSuccessBenefit() {

    return 0;
  }

  /**
   * update model after BS arrival.
   * 
   * @param info
   */
  public void update(final SenseBodyInfo info) {

  }

  /**
   * update model after VI arrival.
   * 
   * @param info
   */
  public void update(final VisualInfo info) {

  }

  /**
   * update model after referee info arrival.
   * 
   * @param info
   */
  public void update(final RefereeInfo info) {

  }

  /**
   * update model after play mode info arrival.
   * 
   * @param info
   */
  public void update(final PlayModeInfo info) {

  }

  /**
   * update model after player say info arrival.
   * 
   * @param info
   */
  public void update(final PlayerSayInfo info) {

  }

  /**
   * home-position from player "number"
   * 
   * @param number
   * @return
   */
  public Vektor getHome(final int number) {

    return this.home[number - 1];
  }

  /**
   * attraction-vektor from player "number"
   * 
   * @param number
   * @return
   */
  public Vektor getAttraction(final int number) {

    return this.attraction[number - 1];
  }

  public boolean PositionsAvailable() {

    return (this.home[1] != null);
  }

  /* (non-Javadoc)
   * @see robocup.component.infotypes.InfoReceiver#update(robocup.component.infotypes.PlayerTypesInfo)
   */
  public void update(final PlayerTypesInfo info) {

    // TODO Auto-generated method stub
    
  }

}
